/* 
  对模板进行编译处理
  > 将template转化成ast语法树
  > 生成render方法（render方法执行后的返回结果就是 虚拟DOM）
*/
import { parseHTML } from "./parse";

/* 生成属性 */
function genProps(attrs) {
  let str = ""; // {name.value}
  for (let i = 0; i < attrs.length; i++) {
    let attr = attrs[i];
    if (attr.name === "style") {
      // color:red;background:red => { color:'red' }
      let obj = {};

      // qs库
      attr.value.split(";").forEach((item) => {
        let [key, value] = item.split(":");
        key = key.trim();
        value = value.trim();
        obj[key] = value;
      });
      attr.value = obj;
    }

    str += `${attr.name}:${JSON.stringify(attr.value)},`; //a:b,c:d, 最后多一个逗号
  }

  return `{${str.slice(0, -1)}}`;
}

/* 生成children */
function genChildren(children) {
  // 有孩子将孩子映射为一个个元素
  return children.map((child) => gen(child)).join(",");
}

// {{xxx}}
const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g;
function gen(node) {
  // 如果是元素
  if (node.type === 1) {
    return codegen(node);
  } else {
    // 文本
    let text = node.text;
    if (!defaultTagRE.test(text)) {
      // 纯文本
      return `_v(${JSON.stringify(text)})`;
    } else {
      // _v(_s(name) + 'hello' + _s(name))  数据分割
      let tokens = [];
      let match;
      // 最后匹配的位置
      let lastIndex = 0;
      // 用了exec 正则中有/g, 遍历时记得重置一下
      defaultTagRE.lastIndex = 0;
      // eg. hello {{name}} {{age}}
      while ((match = defaultTagRE.exec(text))) {
        // 拿到匹配的位置，截出来
        let index = match.index;
        if (index > lastIndex) {
          tokens.push(JSON.stringify(text.slice(lastIndex, index)));
        }

        tokens.push(`_s(${match[1].trim()})`);

        lastIndex = index + match[0].length;
      }
      // 将最后的都截取
      if (lastIndex < text.length) {
        tokens.push(JSON.stringify(text.slice(lastIndex)));
      }
      return `_v(${tokens.join("+")})`;
    }
  }
}

/* 生成代码 */
function codegen(ast) {
  let code = `
    _c('${ast.tag}', 
    ${ast.attrs.length ? genProps(ast.attrs) : "null"}
    ${ast.children.length ? "," + genChildren(ast.children) : []})
  `;

  return code;
}

/* 对模板进行编译处理 */
// > 模板引擎的实现原理都是 with + new Function
export function compileToFunction(template) {
  // 1. 将template转化成ast语法树
  let ast = parseHTML(template);

  // 将ast树进行代码生成，变成字符串类型
  let code = codegen(ast);
  // 再对生成的代码添加with，形参添加个this，执行with里面的代码都会从this里面取值
  // > 这样取值方便，with作用域下的属性
  code = `with(this){
    const vnode = ${code}
    return vnode
  }`;
  // 直接写return ${code}执行不了，我赋值了一个变量再执行就能执行到了

  // 2. 生成render方法（render方法执行后的返回结果就是 虚拟DOM）
  let render = new Function(code);

  // render.call(vm);
  return render;
}
/* 
  function anonymous(
  ) {
  with(this){
    return 
      _c(
        "div",
        { id: "app", style: { color: " red" } },
        _v("       "),
        _c("div", null, _c("div", null, _v(_s(name) + " jjj " + _s(age)))),
        _v("       "),
        _c("span", { style: { color: " red" } }, _v(_s(address.content))),
        _v("     ")
      );
  }
 */
